#!/usr/bin/python3
# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*-

import abc
import enum


class EgressBase(abc.ABC):

    def __init__(self, cost):
        self._cost = cost               # None means unknown
    
    @property
    def cost(self):
        return self._cost


class EgressIpHost(EgressBase):

    def __init__(self, target_prefix, exclude_list=[], cost=None):
        super().__init__(cost)
        self._cidr = target_prefix
        self._exclude = exclude_list

    @property
    def target_prefix(self):
        return self._cidr

    @property
    def exclude_list(self):
        return self._exclude


class EgressDomainHost(EgressBase):

    def __init__(self, target_domain, exclude_list=[], cost=None):
        super().__init__(cost)
        self._domain = target_domain
        self._exclude = exclude_list

    @property
    def target_domain(self):
        return self._domain

    @property
    def exclude_list(self):
        return self._exclude


class EgressIpService(EgressBase):

    def __init__(self, target_prefix, service, exclude_list=[], cost=None):
        super().__init__(cost)
        self._prefix = target_prefix
        self._service_name = service
        self._exclude = exclude_list

    @property
    def target_prefix(self):
        return self._prefix

    @property
    def service(self):
        return self._service_name

    @property
    def exclude_list(self):
        return self._exclude


class EgressDomainService(EgressBase):

    def __init__(self, target_domain, service, exclude_list=[], cost=None):
        super().__init__(cost)
        self._domain = target_domain
        self._service_name = service
        self._exclude = exclude_list

    @property
    def target_domain(self):
        return self._domain

    @property
    def service(self):
        return self._service_name

    @property
    def exclude_list(self):
        return self._exclude


class EgressProviderBase(abc.ABC):

    def __init__(self, parents, node_uuid):
        assert all(isinstance(p, EgressProviderBase) for p in parents)
        self._parents = parents
        self._node_uuid = node_uuid

    @property
    def parents(self):
        return self._parents

    @property
    def node_uuid(self):
        return self._node_uuid


class EgressProviderInterface(EgressProviderBase):

    def __init__(self, parents, node_uuid, interface_name, interface_l2_address):
        assert len(parents) == 0
        super().__init__(parents, node_uuid)
        self._ifname = interface_name
        self._l2addr = interface_l2_address

    @property
    def interface_name(self):
        return self._ifname
    
    @property
    def interface_l2_address(self):
        return self._l2addr


class EgressProviderInterfaceGateway(EgressProviderBase):

    class GatewayType(enum.Enum):
        WIRED_STATIC = "wired-static"
        WIRED_DHCP = "wired-dhcp"
        WIRED_PPP = "wired-ppp"
        WIRELESS_AP = "wireless-ap"

    def __init__(self, parents, node_uuid, interface_name, interface_l2_address, gateway_type, gateway_identifier):
        assert len(parents) > 0
        assert gateway_type in self.GatewayType
        super().__init__(parents, node_uuid)
        self._ifname = interface_name
        self._l2addr = interface_l2_address
        self._type = gateway_type
        self._id = gateway_identifier

    @property
    def interface_name(self):
        return self._ifname

    @property
    def interface_l2_address(self):
        return self._l2addr

    @property
    def gateway_type(self):
        return self._type

    @property
    def gateway_identifier(self):
        return self._id


class EgressProviderObConn(EgressProviderBase):

    def __init__(self, parents, node_uuid, obconn_uuid):
        super().__init__(parents, node_uuid)
        self._uuid = obconn_uuid

    @property
    def obconn_uuid(self):
        return self._uuid


class EgressProviderIpHost(EgressProviderBase, EgressIpHost):

    class SpecialProtocol(enum.Enum):
        HTTP_PROXY = "http-proxy"
        SOCKS_PROXY = "socks-proxy"

    def __init__(self, parents, node_uuid, target_prefix, exclude_list=[], special_protocol=None):
        assert len(parents) > 0
        assert special_protocol is None or special_protocol in self.SpecialProtocol
        EgressProviderBase.__init__(self, parents, node_uuid)
        EgressIpHost.__init__(self, target_prefix, exclude_list)
        self._protocol = special_protocol
    
    @property
    def special_protocol(self):
        return self._protocol


class EgressProviderDomainHost(EgressProviderBase, EgressDomainHost):

    SpecialProtocol = EgressProviderIpHost.SpecialProtocol

    def __init__(self, parents, node_uuid, target_domain, exclude_list=[], special_protocol=None):
        assert len(parents) > 0
        assert special_protocol is None or special_protocol in self.SpecialProtocol
        EgressProviderBase.__init__(self, parents, node_uuid)
        EgressDomainHost.__init__(self, target_domain, exclude_list)
        self._protocol = special_protocol
    
    @property
    def special_protocol(self):
        return self._protocol


class EgressProviderIpService(EgressProviderBase, EgressIpService):

    SpecialProtocol = EgressProviderIpHost.SpecialProtocol

    def __init__(self, parents, node_uuid, target_prefix, service, exclude_list=[], special_protocol=None):
        assert len(parents) > 0
        assert special_protocol is None or special_protocol in self.SpecialProtocol
        EgressProviderBase.__init__(self, parents, node_uuid)
        EgressIpService.__init__(self, target_prefix, service, exclude_list)
        self._protocol = special_protocol

    @property
    def special_protocol(self):
        return self._protocol


class EgressProviderDomainService(EgressProviderBase, EgressDomainService):

    SpecialProtocol = EgressProviderIpHost.SpecialProtocol

    def __init__(self, parents, node_uuid, target_domain, service, exclude_list=[], special_protocol=None):
        assert len(parents) > 0
        assert special_protocol is None or special_protocol in self.SpecialProtocol
        EgressProviderBase.__init__(self, parents, node_uuid)
        EgressDomainService.__init__(self, target_domain, service, exclude_list)
        self._protocol = special_protocol

    @property
    def special_protocol(self):
        return self._protocol


class EgressEntityBase:
    
    def __init__(self, egresses, providers):
        assert egresses is None or (len(egresses) > 0 and all(isinstance(e, EgressBase) for e in egresses))
        assert providers is None or (len(providers) > 0 and all(isinstance(p, EgressProviderBase) for p in providers))
        self._egresses = egresses
        self._providers = providers
    
    @property
    def egresses(self):
        return self._egresses
    
    @property
    def providers(self):
        return self._providers


class EgressEntityIpRoutes(EgressEntityBase):

    def __init__(self, target_prefixes, egresses=None, providers=None):
        super().__init__(egresses, providers)
        self._cidr = target_prefixes

    @property
    def target_prefixes(self):
        return self._cidr


class EgressEntityIpRoutesWithDnsService(EgressEntityBase):

    # FIXME: dns part should be more complex

    class Protocol(enum.Enum):
        DNS = "dns"
        DNS_OVER_HTTPS = "dns-over-https"
        DNS_OVER_TLS = "dns-over-tls"
        DNS_CRYPT = "dns-crypt"

    def __init__(self, target_prefixes, servers, protocol=Protocol.DNS, egresses=None, providers=None):
        super().__init__(egresses, providers)
        self._cidr = target_prefixes
        self._servers = servers
        self._protocol = protocol

    @property
    def target_prefixes(self):
        return self._cidr

    @property
    def servers(self):
        return self._servers

    @property
    def protocol(self):
        return self._protocol


class EgressEntityProxyService(EgressEntityBase):

    class ProxyType(enum.Enum):
        HTTP_PROXY = "http-proxy"
        SOCKS_PROXY = "socks-proxy"

    def __init__(self, proxy_type, ip, port, egresses=None, providers=None):
        assert proxy_type in self.ProxyType
        super().__init__(egresses, providers)
        self._type = proxy_type
        self._ip = ip
        self._port = port

    @property
    def proxy_type(self):
        return self._type
    
    @property
    def ip(self):
        return self._ip
    
    @property
    def port(self):
        return self._port


class EgressEntityPacScript(EgressEntityBase):

    def __init__(self, content, egresses=None, providers=None):
        super().__init__(egresses, providers)
        self._content = content

    @property
    def content(self):
        return self._content
